/*
 * Copyright (c) 2010 Nicolas George
 * Copyright (c) 2011 Stefano Sabatini
 * Copyright (c) 2014 Anshul Maheshwari
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.11pmb071
 *
 */

#include "overlay.h"
#include <fstream>
/** The output bit rate in kbit/s */
#define OUTPUT_BIT_RATE 48000
/** The number of output channels */
#define OUTPUT_CHANNELS 2
/** The audio sample output format */
#define OUTPUT_SAMPLE_FORMAT AV_SAMPLE_FMT_S16



#define STREAM_FRAME_RATE 30
#define STREAM_PIX_FMT    AV_PIX_FMT_YUV420P
#define STREAM_HEIGHT 480
#define STREAM_WIDTH 640

#include "osdependent_func.h"

static FILE *flog;

using namespace std;
static const AVClass av_generator_context_class =
{
/*.class_name     = */"GeneratorCtx",

};

/** Initialize a FIFO buffer for the audio samples to be encoded. */
static int init_fifo(AVAudioFifo **fifo)
{
    /** Create the FIFO buffer based on the specified output sample format. */
    if (!(*fifo = av_audio_fifo_alloc(OUTPUT_SAMPLE_FORMAT, OUTPUT_CHANNELS, 1))) {
        av_log(NULL, AV_LOG_ERROR, "Could not allocate FIFO\n");
        return AVERROR(ENOMEM);
    }
    return 0;
}

/** Un Initialize a FIFO buffer for the audio samples to be encoded. */
static void dinit_fifo(AVAudioFifo **fifo)
{
	if(*fifo)
	{
		av_audio_fifo_free(*fifo);
		*fifo = NULL;
	}
}
/** Add converted input audio samples to the FIFO buffer for later processing. */
static int add_samples_to_fifo(AVAudioFifo *fifo,
                               uint8_t **converted_input_samples,
                               const int frame_size)
{
    int error;

    /**
     * Make the FIFO as large as it needs to be to hold both,
     * the old and the new samples.
     */
    if ((error = av_audio_fifo_realloc(fifo, av_audio_fifo_size(fifo) + frame_size)) < 0) {
    	av_log(NULL, AV_LOG_ERROR, "Could not reallocate FIFO\n");
        return error;
    }

    /** Store the new samples in the FIFO buffer. */
    if (av_audio_fifo_write(fifo, (void **)converted_input_samples,
                            frame_size) < frame_size) {
    	av_log(NULL, AV_LOG_ERROR, "Could not write data to FIFO\n");
        return AVERROR_EXIT;
    }
    return 0;
}

static void log_cb(void* ptr, int level, const char* fmt, va_list vl)
{
	if (level > av_log_get_level())
		return;

#ifndef WINDOWS
	flockfile(flog);
#endif
	const char*name = NULL;
	if (ptr)
		name = av_default_item_name(ptr);

	if (level == AV_LOG_PANIC)
		fprintf(flog, "[panic][%s] ", name);
	else if (level == AV_LOG_FATAL)
		fprintf(flog, "[fatal][%s] ", name);
	else if (level == AV_LOG_ERROR)
	{
		fprintf(flog, "[error][%s] ", name);
	}
	else if (level == AV_LOG_WARNING)
		fprintf(flog, "[warning][%s] ", name);
	else if (level == AV_LOG_INFO)
		fprintf(flog, "[info][%s] ", name);
	else if (level == AV_LOG_DEBUG)
		fprintf(flog, "[debug][%s] ", name);

	vfprintf(flog, fmt, vl);
#ifndef WINDOWS
	funlockfile(flog);
#endif
	fflush(flog);
}

static int max_lenword(const char *istr)
{
	int len = 0;
	int tlen = 0;
	char*save_str = NULL;
	char *word = NULL;
	//maximum length  character
	char *str = av_strdup(istr);
	word = av_strtok(str, " \n\t\f", &save_str);

	while (word)
	{
		tlen = strlen(word);
		if (len < tlen)
			len = tlen;
		word = av_strtok(NULL, " \n\t\f", &save_str);
	}
	av_freep(&str);

	return len;
}
/*
 * Test Text
 * "String geometry\n\nThis is a long string meant to display in a\
 * Tk text widget. Some lines are very long, and then again there are paragraph\
 * breaks.\n\nThe text widget shall wrap words, but be neither too big nor too\
 * small, and have a pleasing aspect ratio (say, in pixels, 4:3 or 16:9)."
 */
static void str_rearrange(std::string &istr, double *w, double *h)

{

	int nl_count = 0;
	double test_len = (double) istr.length();

	double w0 = 0;
	double h0 = 0;

	std::vector<std::string> v;

	for (unsigned int i = 0; i < istr.length(); i++)
	{
		if (istr.at(i) == '\n')
			nl_count++;
	}
	//max length word
	for (unsigned int i = 0; i < istr.length(); i++)
	{
		if (istr.at(i) == '\n')
			nl_count++;
	}

	w0 = round(8.0 / 3.0 * sqrt(test_len));
	h0 = round(3.0 / 8.0 * sqrt(test_len)) + nl_count;

	test_len = max_lenword(istr.c_str());
	if (w0 < test_len)
		w0 = test_len;

	for (unsigned int i = 0, len = 0, si = 0; i < istr.length(); i++, len++)
	{
		if (istr.at(i) == '\n')
		{
			istr.erase(i, 1);
			istr.insert(i, "\r");
			len = 0;
		}
		if (istr.at(i) == ' ')
		{
			if (len > w0)
			{
				istr.erase(si, 1);
				istr.insert(si, "\r");

				len = 0;
			}
			else
				si = i;
		}
	}
	//escape sequence for special character for filter
	for (unsigned int i = 0; i < istr.length(); i++)
	{
		switch (istr.at(i))
		{
		case ',':
		case ';':
		case '[':
		case ']':

			istr.insert(i, "\\");
			i++;
			break;

		case ':':
		case '{':
		case '}':
			istr.insert(i, "\\\\");
			i += 2;
			break;
		case '\'':
			istr.insert(i, "\\\\\\");
			i += 3;
			break;
		case '%':
			istr.insert(i, "\\\\\\\\");
			i += 4;
			break;
//		case '\\':
//			istr.insert(i, "\\\\\\\\\\\\\\");
//			i +=7;
//			break;
		case '\t':
			istr.erase(i, 1);
			istr.insert(i, "    ");
			i += 3;
			break;
		}
	}
	*h = h0;
	*w = w0;
}
static int get_font_size(std::string &test_str)
{

	//1 inch is around 96 pixel
	int w = STREAM_WIDTH;

	double tw = 0;
	double th = 0;

	str_rearrange(test_str, &tw, &th);

	return w / tw;
}
/**
 * xxx we do not support B frame videos
 */
static int write_video_frame(AVFormatContext *oc, AVStream *st, AVFrame *frame)
{
	int ret = 0;
	AVCodecContext *c = st->codec;
	int got_packet = 0;
	char arr_string[128];
	AVPacket pkt;
	av_init_packet(&pkt);
	pkt.data = NULL;    // packet data will be allocated by the encoder
	pkt.size = 0;
	/* encode the image */
	ret = avcodec_encode_video2(c, &pkt, frame, &got_packet);
	if (ret < 0)
	{
		av_log(NULL, AV_LOG_ERROR, "Error encoding video frame: %s\n",
				av_make_error_string(arr_string, 128, ret));
		return -1;
	}
	/* If size is zero, it means the image was buffered. */
	if (!ret && got_packet && pkt.size)
	{
		pkt.stream_index = st->index;

		/* Write the compressed frame to the media file. */
		ret = av_interleaved_write_frame(oc, &pkt);
	}
	else
	{
		ret = 0;
	}
	if (ret != 0)
	{
		av_log(NULL, AV_LOG_ERROR, "Error while writing video frame: %s\n",
				av_make_error_string(arr_string, 128, ret));
		return -1;
	}
	return ret;
}
/**
 * Initialize a temporary storage for the specified number of audio samples.
 * The conversion requires temporary storage due to the different format.
 * The number of audio samples to be allocated is specified in frame_size.
 */
static int init_converted_samples(uint8_t ***converted_input_samples,
                                  AVCodecContext *output_codec_context,
                                  int frame_size)
{
	int ret = 0;
	/**
	 * Allocate as many pointers as there are audio channels.
	 * Each pointer will later point to the audio samples of the corresponding
	 * channels (although it may be NULL for interleaved formats).
	 */
	*converted_input_samples = (uint8_t **)calloc(output_codec_context->channels,sizeof(**converted_input_samples));
	if (*converted_input_samples == NULL)
	{
		av_log(NULL, AV_LOG_ERROR,"Could not allocate converted input sample pointers\n");
		return AVERROR(ENOMEM);
	}
	/**
	 * Allocate memory for the samples of all channels in one consecutive
	 * block for convenience.
	 */
	ret = av_samples_alloc(*converted_input_samples, NULL,output_codec_context->channels,frame_size,output_codec_context->sample_fmt, 0);
	if (ret < 0)
	{
		av_log(NULL, AV_LOG_ERROR,"Could not allocate converted input samples \n");
		av_freep(&(*converted_input_samples)[0]);
		free(*converted_input_samples);
		return ret;
	}
	return ret;
}
/**
 * Un Initialize the temporary storage 
 */
static int dinit_converted_samples(uint8_t **converted_input_samples)
{
	if (converted_input_samples)
	{
		av_freep(&converted_input_samples[0]);
		free(converted_input_samples);
	}
	return 0;
}
static int write_audio_frame(struct GeneratorCtx *ctx)
{
	AVCodecContext *dec_ctx;
	AVCodecContext *enc_ctx;
	int got_packet = 0, ret = 0;
	int got_frame = 0;
	uint8_t **converted_input_samples = NULL;
	AVPacket enc_pkt =
	{ 0 };
	struct AudioContext *aud = &ctx->audio;

	av_init_packet(&enc_pkt);
	dec_ctx = aud->dec_ctx;
	enc_ctx = ctx->oc->streams[1]->codec;

	do
	{
		if (ctx->audio_pkt_size == 0)
		{
			av_init_packet(&aud->dec_pkt);
			ret = av_read_frame(ctx->audio.in_fmt, &aud->dec_pkt);
			if (ret == AVERROR_EOF)
			{
				/* eof file recieved seek to start of audio */
				avformat_seek_file(ctx->audio.in_fmt, -1, 0, 0, 0, 0);
				continue;

			}
			if (ret < 0)
			{
				av_log(ctx, AV_LOG_ERROR, "unable to read audio file\n");
				return ret;
			}
			if (aud->dec_pkt.stream_index != ctx->audio_stream_index)
			{
				continue;
			}
			if (aud->dec_pkt.size == 0)
			{
				av_log(ctx, AV_LOG_ERROR, "unable to read audio file\n");
			}

			ctx->audio_pkt_size = aud->dec_pkt.size;
		}
		ret = avcodec_decode_audio4(dec_ctx, aud->dec_frame, &got_frame,
				&aud->dec_pkt);
                if (ret == AVERROR_INVALIDDATA)
                {
                    ctx->audio_pkt_size -= aud->dec_pkt.size;
                    continue;
                }

		if (ret < 0)
		{
			av_log(ctx, AV_LOG_ERROR, "Error while decoding audio %x  %x\n",ret,AVERROR_INVALIDDATA);
			ctx->audio_pkt_size -= aud->dec_pkt.size;
			return ret;
		}
		ctx->audio_pkt_size -= FFMIN(ret, aud->dec_pkt.size);

		ret = init_converted_samples(&converted_input_samples,enc_ctx,aud->dec_frame->nb_samples);
		if(ret < 0)
		{
			av_log(ctx, AV_LOG_ERROR, "unable to allocate the init sample\n");
		}
		/**
		 * Convert the input samples to the desired output sample format,
		 * using resampler.
		 */
		if(aud->swr_ctx)
		{
			ret = swr_convert(aud->swr_ctx,converted_input_samples,
					aud->dec_frame->nb_samples,
					(const uint8_t**) aud->dec_frame->extended_data,
					aud->dec_frame->nb_samples);
			if (ret < 0)
			{
				av_log(ctx, AV_LOG_ERROR, "Could not convert input samples \n");
				return ret;
			}
		}

		if(aud->swr_ctx)
	        	/** Add the converted input samples to the FIFO buffer for later processing. */
			ret =add_samples_to_fifo(aud->fifo, converted_input_samples,aud->dec_frame->nb_samples);
		else
	        	/** Add the input samples to the FIFO buffer for later processing. */
			ret =add_samples_to_fifo(aud->fifo,aud->dec_frame->extended_data, aud->dec_frame->nb_samples);
        	if (ret != 0)
		{
			av_log(ctx, AV_LOG_ERROR, "error at add_samples_to_fifo\n");
			return ret;
		}

		dinit_converted_samples(converted_input_samples);


	        if(av_audio_fifo_size(aud->fifo) < enc_ctx->frame_size)
        		continue;

		break;
	} while (1);

	if (got_frame)
	{
		/* compute destination number of samples */
		aud->time = ctx->oc->streams[1]->pts.val * av_q2d(ctx->oc->streams[1]->time_base);

		/**
		 * Use the maximum number of possible samples per frame.
		 * If there is less than the maximum possible frame size in the FIFO
		 * buffer use this number. Otherwise, use the maximum possible frame size
		 */

		const int frame_size = FFMIN(av_audio_fifo_size(aud->fifo),enc_ctx->frame_size);
		AVFrame *enc_frame = NULL;

		enc_frame = av_frame_alloc();
		if(NULL == enc_frame)
		{
			av_log(NULL, AV_LOG_ERROR, "encoder frame mot allocated\n");
		}
		enc_frame->nb_samples     = frame_size;
		enc_frame->channel_layout = enc_ctx->channel_layout;
		enc_frame->format         = enc_ctx->sample_fmt;
		enc_frame->sample_rate    = enc_ctx->sample_rate;

		/**
		 * Allocate the samples of the created frame. This call will make
		 * sure that the audio frame can hold as many samples as specified.
		 */
		ret = av_frame_get_buffer(enc_frame, 0);
		if (ret < 0) 
		{
			av_log(NULL, AV_LOG_ERROR, "Could allocate output frame samples\n");
			return ret;
		}

		/**
		 * Read as many samples from the FIFO buffer as required to fill the frame.
		 * The samples are stored in the frame temporarily.
		 */
		if (av_audio_fifo_read(aud->fifo, (void **)enc_frame->data, frame_size) < frame_size) {
			av_log(NULL, AV_LOG_ERROR,"Could not read data from FIFO\n");
			return AVERROR_EXIT;
		}

		ret = avcodec_encode_audio2(enc_ctx, &enc_pkt,
				enc_frame, &got_packet);
		if (ret < 0)
		{
			av_log(ctx, AV_LOG_ERROR, "Error encoding audio frame\n");
			return -1;
		}
		av_frame_free(&enc_frame);

	}
	enc_pkt.stream_index = 1;

	/* Write the compressed frame to the media file. */
	ret = av_interleaved_write_frame(ctx->oc, &enc_pkt);

	if (!ctx->audio_pkt_size)
		av_free_packet(&ctx->audio.dec_pkt);

	av_free_packet(&enc_pkt);
	return ret;
}

/* Add an output stream. */
static AVStream *
add_stream(AVFormatContext *oc, AVCodec **codec, enum AVCodecID codec_id)
{
	AVCodecContext *c = NULL;
	AVStream *st = NULL;

	/* find the encoder */
	*codec = avcodec_find_encoder(codec_id);
	if (!(*codec))
	{
		av_log(NULL, AV_LOG_ERROR, "Could not find encoder for '%s'\n",
				avcodec_get_name(codec_id));
		return NULL;
	}

	st = avformat_new_stream(oc, *codec);
	if (!st)
	{
		av_log(NULL, AV_LOG_ERROR, "Could not allocate stream\n");
		return NULL;
	}
	st->id = oc->nb_streams - 1;
	c = st->codec;

	switch ((*codec)->type)
	{
	case AVMEDIA_TYPE_AUDIO:
		c->sample_fmt =
				(*codec)->sample_fmts ?
						(*codec)->sample_fmts[0] : AV_SAMPLE_FMT_FLTP;

		c->bit_rate = 128000;
		c->sample_rate = 48000;
		c->channels = 2;
		break;

	case AVMEDIA_TYPE_VIDEO:
		c->codec_id = codec_id;

		c->bit_rate = 0;
		/* Resolution must be a multiple of two. */
		c->width = STREAM_WIDTH;
		c->height = STREAM_HEIGHT;
		/* timebase: This is the fundamental unit of time (in seconds) in terms
		 * of which frame timestamps are represented. For fixed-fps content,
		 * timebase should be 1/framerate and timestamp increments should be
		 * identical to 1. */
		c->time_base.den = STREAM_FRAME_RATE;
		c->time_base.num = 1;
		//c->gop_size = 15; /* emit one intra frame every fifteen frames at most */
		c->max_b_frames = 0;
		c->pix_fmt = STREAM_PIX_FMT;
		if (c->codec_id == AV_CODEC_ID_MPEG1VIDEO)
		{
			/* Needed to avoid using macroblocks in which some coeffs overflow.
			 * This does not happen with normal video, it just happens here as
			 * the motion of the chroma plane does not match the luma plane. */
			c->mb_decision = 2;
		}
		break;

	default:
		break;
	}

	/* Some formats want stream headers to be separate. */
	if (oc->oformat->flags & AVFMT_GLOBALHEADER)
		c->flags |= CODEC_FLAG_GLOBAL_HEADER;

	return st;
}

static void dinit_decoder(struct GeneratorCtx *ctx)
{

	if (ctx->dec_ctx)
	{
		avcodec_close(ctx->dec_ctx);
		ctx->dec_ctx = NULL;
	}

	if(ctx->vfmt)
		avformat_close_input(&ctx->vfmt);
}

static int init_decoder(struct GeneratorCtx *ctx, const char *in_fname)
{

	int ret = -1;
	AVCodec *dec = NULL;
	int video_stream_index = -1;

	if(ctx->vfmt)
	{
		av_log(ctx, AV_LOG_ERROR, "probably forgotten to dinit_decoder\n");
		return ret;
	}

	if ((ret = avformat_open_input(&ctx->vfmt, in_fname, NULL, NULL)) < 0)
	{
		av_log(ctx, AV_LOG_ERROR, "Decoder Can't open %s\n", in_fname);
		return ret;
	}

	if ((ret = avformat_find_stream_info(ctx->vfmt, NULL)) < 0)
	{
		av_log(ctx, AV_LOG_ERROR, "Cannot find stream information\n");
		avformat_close_input(&ctx->vfmt);
		return ret;
	}

	/* select the video stream */
	ret = av_find_best_stream(ctx->vfmt, AVMEDIA_TYPE_VIDEO, -1, -1, &dec, 0);
	if (ret < 0)
	{
		av_log(ctx, AV_LOG_ERROR, "no video in input file\n");
		avformat_close_input(&ctx->vfmt);
		return ret;
	}

	video_stream_index = ret;
	ctx->dec_ctx = ctx->vfmt->streams[video_stream_index]->codec;

	/* init the image  decoder */
	if ((ret = avcodec_open2(ctx->dec_ctx, dec, NULL)) < 0)
	{
		av_log(ctx, AV_LOG_ERROR, "Cannot open video decoder\n");
		return ret;
	}

	return ret;

}
static void dinit_encoder(AVFormatContext **oc)
{
	AVOutputFormat *fmt;
	AVStream *st;
	AVFormatContext *loc = *oc;
	fmt = loc->oformat;

	av_write_trailer(loc);
	/* Close each codec. */
	for (unsigned int i = 0; i < loc->nb_streams; i++)
	{
		st = loc->streams[i];
		avcodec_close(st->codec);
	}

	if (!(fmt->flags & AVFMT_NOFILE))
	{
		/* Close the output file. */
		avio_close(loc->pb);
	}

	avformat_free_context(loc);
}

static int init_encoder(AVFormatContext **oc, const char* oname)
{
	int ret = 0;
	char arr_string[128] = "";
	AVOutputFormat *fmt;
	AVCodec *audio_codec, *video_codec;
	AVStream *vst;
	AVStream *ast;


	/* allocate the output media context */
	avformat_alloc_output_context2(oc, NULL, NULL, oname);
	if (!*oc)
	{
		av_log(NULL, AV_LOG_ERROR, "Could not deduce output format\n");
		return -1;
	}
	AVFormatContext *loc = *oc;
	fmt = loc->oformat;
	if (fmt->video_codec != AV_CODEC_ID_NONE)
	{
		vst = add_stream(loc, &video_codec, fmt->video_codec);
	}

	if (fmt->audio_codec != AV_CODEC_ID_NONE)
	{
		ast = add_stream(loc, &audio_codec, fmt->audio_codec);
	}
	if (!ast || !vst)
	{
		return -1;
	}

	/* open the codec */
	ret = avcodec_open2(vst->codec, video_codec, NULL);
	if (ret < 0)
	{
		av_log(NULL, AV_LOG_ERROR, "Could not open video codec: %s\n",
				av_make_error_string(arr_string, 128, ret));
		return -1;
	}

	ast->codec->channel_layout = 3;

	/* open the audio codec */
	ret = avcodec_open2(ast->codec, audio_codec, NULL);
	if (ret < 0)
	{
		av_log(NULL, AV_LOG_ERROR, "Could not open audio codec: %s\n",
				av_make_error_string(arr_string, 128, ret));
		return -1;
	}

	/* open the output file, if needed */
	if (!(fmt->flags & AVFMT_NOFILE))
	{
		ret = avio_open(&loc->pb, oname, AVIO_FLAG_WRITE);
		if (ret < 0)
		{
			av_log(NULL, AV_LOG_ERROR, "Could not open '%s': %s\n", oname,
					av_make_error_string(arr_string, 128, ret));
			//xxx decide kya karna hai
			return -1;
		}
	}

	/* Write the stream header, if any. */
	ret = avformat_write_header(loc, NULL);
	if (ret < 0)
	{
		av_log(NULL, AV_LOG_ERROR, "Error occurred when writing header: %s\n",
				av_make_error_string(arr_string, 128, ret));
		dinit_encoder(oc);
	}

	return ret;
}
static void dinit_filters(struct GeneratorCtx *ctx)
{
	avfilter_graph_free(&ctx->filter_graph);
}
static int init_filters(struct GeneratorCtx *ctx)
{
	char args[512];
	char fd_args[512];
#ifdef WINDOWS
	char font_file[PATH_LEN] = "";
#endif

	int ret = 0;
	int len = 0;

	AVFilter *buffersrc = avfilter_get_by_name("buffer");
	AVFilter *buffersink = avfilter_get_by_name("buffersink");
	AVFilterInOut *outputs = avfilter_inout_alloc();
	AVFilterInOut *inputs = avfilter_inout_alloc();
	AVBufferSinkParams *buffersink_params;
	enum AVPixelFormat pix_fmts[] =
	{ AV_PIX_FMT_GRAY8, AV_PIX_FMT_NONE };

	struct DrawContext *draw = &ctx->draw;

	len += snprintf(fd_args, sizeof(fd_args), "format=yuv420p,scale=640:480");
	//Initialize draw text filter if text need to be applied

	if (*draw->text)
	{

		std::string text(draw->text);
		int fsize = 100;

		len += snprintf(fd_args + len, sizeof(fd_args), ",drawtext=");
#ifdef WINDOWS
		for(int i =0,j=0;i<PATH_LEN && ctx->vg_install_path[i] != '\0';i++,j++)
		{
			switch(ctx->vg_install_path[i])
			{
				case '\\':
				case ':':
					strcpy(font_file+j,"\\\\\\");
					j += 3;
					break;
			}
			font_file[j] = ctx->vg_install_path[i];

		}
		len += snprintf(fd_args + len, sizeof(fd_args),"fontfile=%s\\\\\\\\font\\\\\\\\arialbd.ttf",font_file);
#else
		len += snprintf(fd_args + len, sizeof(fd_args),"fontfile=%s/font/arialbd.ttf",ctx->vg_install_path);
#endif

		if (draw->xpos == -1 && draw->ypos  == -1)
		{
			//add font size
			fsize = get_font_size(text);
			len += snprintf(fd_args + len,  sizeof(fd_args),":x=(w-tw)/2:y=(h-th)/2");
		}

		else if (draw->xpos == -1 && draw->ypos  != -1)
		{
			//add font size
			fsize = (2 * (550 / strnlen(draw->text,draw->text_len)));
			len += snprintf(fd_args + len,  sizeof(fd_args),":x=(w-tw)/2");
			len += snprintf(fd_args + len, sizeof(fd_args),":y=%d",
					draw->ypos);

		}
		else if(draw->xpos != -1 && draw->ypos  == -1)
		{
			//add font size
			fsize = (2 * (550 / strnlen(draw->text,draw->text_len)));
			len += snprintf(fd_args + len, sizeof(fd_args), ":x=%d",
					draw->xpos);
			len += snprintf(fd_args + len, sizeof(fd_args),":y=(h-th)/2");


		}
		else 
		{
			//add font size
			fsize = (2 * (550 / strnlen(draw->text,draw->text_len)));
			len += snprintf(fd_args + len, sizeof(fd_args), ":x=%d:y=%d",
					draw->xpos,draw->ypos);
		}

		len += snprintf(fd_args + len, sizeof(fd_args),":fontsize=%d",fsize);
		//set font color of text
		len += snprintf(fd_args + len, sizeof(fd_args),":fontcolor=%06x",draw->color);

		//set shadow color
		len += snprintf(fd_args + len, sizeof(fd_args),":shadowcolor=%06x",draw->shadowcolor);

		//set shadow x shift
		len += snprintf(fd_args + len, sizeof(fd_args),":shadowx=%d",draw->shadowx);
		//set shadow y shift
		len += snprintf(fd_args + len, sizeof(fd_args),":shadowy=%d",draw->shadowy);

		//adding text for draw text filter
		len += snprintf(fd_args + len, sizeof(fd_args), ":text=%s",text.c_str());
	}

	ctx->filter_graph = avfilter_graph_alloc();
	/* buffer video source: the decoded frames from the decoder will be inserted here. */
	snprintf(args, sizeof(args),
			"video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
			ctx->dec_ctx->width, ctx->dec_ctx->height, ctx->dec_ctx->pix_fmt,
			ctx->dec_ctx->time_base.num, ctx->dec_ctx->time_base.den,
			ctx->dec_ctx->sample_aspect_ratio.num,
			ctx->dec_ctx->sample_aspect_ratio.den);

	ret = avfilter_graph_create_filter(&ctx->buffersrc, buffersrc, "in", args,
	NULL, ctx->filter_graph);
	if (ret < 0)
	{
		av_log(NULL, AV_LOG_ERROR, "Cannot create buffer source\n");
		return ret;
	}

	/* buffer video sink: to terminate the filter chain. */
	buffersink_params = av_buffersink_params_alloc();
	buffersink_params->pixel_fmts = pix_fmts;
	ret = avfilter_graph_create_filter(&ctx->buffersink, buffersink, "out",
	NULL, buffersink_params, ctx->filter_graph);
	av_free(buffersink_params);
	if (ret < 0)
	{
		av_log(NULL, AV_LOG_ERROR, "Cannot create buffer sink\n");
		return ret;
	}
	/* Endpoints for the filter graph. */
	outputs->name = av_strdup("in");
	outputs->filter_ctx = ctx->buffersrc;
	outputs->pad_idx = 0;
	outputs->next = NULL;

	inputs->name = av_strdup("out");
	inputs->filter_ctx = ctx->buffersink;
	inputs->pad_idx = 0;
	inputs->next = NULL;

	if ((ret = avfilter_graph_parse_ptr(ctx->filter_graph,fd_args,
			&inputs, &outputs, NULL)) < 0)
		return ret;

	if ((ret = avfilter_graph_config(ctx->filter_graph, NULL)) < 0)
		return ret;
	return 0;
}
/**
 * this function add the scpiefied phase to video
 */
extern "C" int add_phase(struct GeneratorCtx *ctx, const char*img_directory,
		const char*overlay_text, int img_count, int min_time, int max_time,
		int textcolor, int textx,int texty,int shadowcolor,
		int shadowx,int shadowy)
{

	int len = 0;
	int count = 0;
	int got_frame = 0;
	unsigned int ran_num = 0;
	unsigned int seed = 0;
	char arr[PATH_LEN];
	AVStream *vst;
	int ret = 0;

	struct timespec t =
	{ 0, 0 };
	int diff = max_time - min_time;


	if (!clock_gettime(CLOCK_REALTIME, &t))
		seed = t.tv_nsec;
	else
		seed = (int) clock();

	len = av_strlcpy(arr, img_directory, PATH_LEN);

#ifdef WINDOWS
	len += av_strlcpy(arr + len, "//", PATH_LEN);
#else
	len += av_strlcpy(arr + len, "/", PATH_LEN);
#endif

	std::vector<std::string> file_list;
	DIR* image_dir = NULL;
	struct dirent *pDirent;
	AVPacket packet;


	image_dir = opendir(img_directory);
	if (image_dir == NULL)
	{
		av_log(ctx, AV_LOG_ERROR,
						"unable to open img_directory %s\n",img_directory);
		return -1;
	}

	while ((pDirent = readdir(image_dir)) != NULL)
	{
		int file_matched = 0;
		if (av_match_ext(pDirent->d_name, "jpg"))
		{
			file_matched = 1;
		}
		else if (av_match_ext(pDirent->d_name, "jpeg"))
		{
			file_matched = 1;
		}
		if (av_match_ext(pDirent->d_name, "JPG"))
		{
			file_matched = 1;
		}
		else if (av_match_ext(pDirent->d_name, "JPEG"))
		{
			file_matched = 1;
		}

		if (file_matched)
		{
			if (av_strlcpy(arr + len, pDirent->d_name, PATH_LEN) >= PATH_LEN)
				av_log(ctx, AV_LOG_ERROR,
						"absolute path exceed the max path length \n");
			file_list.push_back(arr);
			count++;
		}

	}
	closedir(image_dir);
	if (!count)
	{
		return -1;
	}

	ran_num = seed;//rand_r(&seed);

	vst = ctx->oc->streams[0];


	int itext = 0;
	double video_time = 0;
	int i =0;
	for ( i = 0; i < img_count; i++)
	{
		std::string sel_file = file_list.at((ran_num + i) % count);

		ret = init_decoder(ctx, sel_file.c_str());
		if (ret < 0)
		{
			av_log(ctx, AV_LOG_ERROR, "Decoder does not support %s\n",sel_file.c_str());
			img_count++;
			continue;
		}

		video_time = ctx->ptime;
		if(diff)
		    ctx->ptime += min_time + ( (ran_num % diff +1) );
		else
		    ctx->ptime +=  min_time;

		char str[128] = "";
		for(int itok =itext; (itok -itext) <128;itok++)
		{
			if (overlay_text[itok] == ',')
			{
				str[itok] = '\0';
				itext = itok+1;
				break;
			}
			else if( overlay_text[itok] == '\0')
			{
				itext = 0;
				break;
			}
			else
				str[itok -itext] = overlay_text[itok];
		}


		ctx->draw.text = str;
		ctx->draw.text_len = 128;

		ctx->draw.color = textcolor;
		ctx->draw.xpos = textx;
		ctx->draw.ypos = texty;
		ctx->draw.shadowcolor = shadowcolor;
		ctx->draw.shadowx = shadowx;
		ctx->draw.shadowy = shadowy;

		ret = init_filters(ctx);
		if (ret < 0)
		{
			av_log(ctx, AV_LOG_ERROR, "could not initialize filter\n");
			break;
		}

		if ((ret = av_read_frame(ctx->vfmt, &packet)) < 0)
		{
			av_log(ctx, AV_LOG_ERROR, "unable to find image\n");
			break;
		}
		while (ctx->ptime > video_time)
		{

			if (ctx->audio.time <= video_time && ctx->audio.in_fmt)
			{
				ret = write_audio_frame(ctx);
				if (ret < 0)
				{
					ret = -1;
					break;
				}
				continue;
			}
			//TODO use got frame variable for videos
			ret = avcodec_decode_video2(ctx->dec_ctx, ctx->InFrame, &got_frame,
					&packet);
			if (ret < 0)
			{
				av_log(NULL, AV_LOG_ERROR, "Error decoding video\n");
				return ret;
			}

			if (av_buffersrc_add_frame_flags(ctx->buffersrc, ctx->InFrame,
					AV_BUFFERSRC_FLAG_KEEP_REF) < 0)
			{
				av_log(NULL, AV_LOG_ERROR,
						"Error while feeding the filtergraph\n");
			}

			/* pull filtered frames from the filtergraph */
			while (1)
			{
				ret = av_buffersink_get_frame(ctx->buffersink, ctx->OutFrame);
				if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
				{
					ret = 0;
					break;
				}
				if (ret < 0)
				{
					av_log(ctx, AV_LOG_ERROR, "nothing in buffer sink\n");
					ret = -1;
					break;
				}
				video_time = vst ? vst->pts.val * av_q2d(vst->time_base) : 0.0;
				ctx->OutFrame->pts = ctx->cur_pts;
				/*
				 * Set the picture type to none, other wise video would have 
				 * large size with all I frame and gop will have no effect
				 */
				ctx->OutFrame->pict_type = AV_PICTURE_TYPE_NONE;
				ret = write_video_frame(ctx->oc, vst, ctx->OutFrame);
				if(ret < 0)
				{
				    av_log(ctx, AV_LOG_ERROR, "%d Unable to write video frame %s\n",ret,sel_file.c_str());
				    break;

				}
				ctx->cur_pts += av_rescale_q(1, vst->codec->time_base,
						vst->time_base);
				av_frame_unref(ctx->OutFrame);
			}
			av_frame_unref(ctx->InFrame);
			if(ret < 0)
			{
			    break;
			}			

		}
		dinit_filters(ctx);
		dinit_decoder(ctx);

		av_free_packet(&packet);

		//Dont Annoy by givinng error again again just dont use rest images if there is an error
		if(ret < 0)
			break;

	}
	if(i != img_count)
	{
		dinit_filters(ctx);
		dinit_decoder(ctx);
	}

	return ret;
}

static struct SwrContext *
make_swr(struct AVCodecContext *in, AVCodecContext *out)
{
	int ret = 0;
	struct SwrContext *swr = NULL;

	/* set options */
	swr = swr_alloc_set_opts(NULL, out->channel_layout, out->sample_fmt,
			out->sample_rate, in->channel_layout, in->sample_fmt,
			in->sample_rate, 0, NULL);
	if (!swr)
	{
		av_log(NULL, AV_LOG_ERROR, "Could not allocate swr\n");
		return NULL;
	}

	/* initialize the resampling context */
	if ((ret = swr_init(swr)) < 0)
	{
		av_log(NULL, AV_LOG_ERROR,
				"Failed to initialize the resampling context\n");
		swr_free(&swr);
	}

	return swr;
}
static void destroy_swr(struct SwrContext **swr)
{

	swr_free(swr);
}
extern "C" void unset_audio(struct AudioContext *ctx)
{

	if (ctx->swr_ctx)
		destroy_swr(&ctx->swr_ctx);

	if (ctx->dec_ctx)
	{
		avcodec_close(ctx->dec_ctx);
		ctx->dec_ctx = NULL;
	}

	if (ctx->in_fmt)
	{

		avformat_close_input(&ctx->in_fmt);
	}
	dinit_fifo(&ctx->fifo);

}
extern "C" int set_audio(struct GeneratorCtx *ctx, const char*audio_file)
{
	int ret = 0;
	AVCodec *dec = NULL;

	struct AudioContext* lctx = &ctx->audio;

//compare the name of audio file if name is same then no need to open the file again
	if (strcmp(lctx->fname, audio_file))
	{

		unset_audio(lctx);

		ret = avformat_open_input(&lctx->in_fmt, audio_file, NULL, NULL);
		if (ret < 0)
		{
			av_log(NULL, AV_LOG_ERROR, "Decoder Can't open %s\n", audio_file);
			goto err;
		}

		if ((ret = avformat_find_stream_info(lctx->in_fmt, NULL)) < 0)
		{
			av_log(NULL, AV_LOG_ERROR, "Cannot find stream information of %s\n",audio_file);
			goto err;
		}

		/* select the audio stream */
		ret = av_find_best_stream(lctx->in_fmt, AVMEDIA_TYPE_AUDIO, -1, -1,
				&dec, 0);
		if (ret < 0)
		{
			av_log(NULL, AV_LOG_ERROR, "no audio in input file %s \n",audio_file);
			goto err;
		}
		ctx->audio_stream_index = ret;
		lctx->dec_ctx = lctx->in_fmt->streams[ret]->codec;

		/* open it */
		ret = avcodec_open2(lctx->dec_ctx, dec, NULL);
		if (ret < 0)
		{
			av_log(NULL, AV_LOG_ERROR, "Unable to open audio decoder\n");
			goto err;
		}

//		/* Check whether both have same sample format */
		if (lctx->dec_ctx->sample_fmt != ctx->oc->streams[1]->codec->sample_fmt)
		{
			lctx->swr_ctx = make_swr(lctx->dec_ctx, ctx->oc->streams[1]->codec);
		}
		else
		{
			lctx->swr_ctx = NULL;
		}

		/** Initialize the FIFO buffer to store audio samples to be encoded. */
		if (init_fifo(&lctx->fifo))
			goto err;


	}

	return ret;
	err: unset_audio(lctx);
	return -1;
}

extern "C" struct GeneratorCtx *
init_generator(const char* oname)
{
	int ret = 0;
	struct GeneratorCtx *ctx;
	char logpath[PATH_LEN];
#ifdef WINDOWS
	char *vgm_i_path = (char*)malloc(PATH_LEN);
	ret = GetEnvironmentVariable("VGM_INSTALL_PATH",vgm_i_path,PATH_LEN);
	if(0 == ret)
	{
		printf( "installation path environment variable not set \n");
		return NULL;
	}

#else
	char *vgm_i_path = NULL;
	vgm_i_path = getenv ("VGM_INSTALL_PATH");
	if(NULL == vgm_i_path)
	{
		printf("installation path environment variable not set \n");
		return NULL;
	}
#endif

#ifdef WINDOWS
	snprintf(logpath,PATH_LEN,"%s//flog.txt",vgm_i_path);
#else
	snprintf(logpath,PATH_LEN,"%s/flog.txt",vgm_i_path);
#endif
	flog = fopen(logpath, "w");
	if (!flog)
	{
		printf("Error:Unable to open ffmpeg log file %s\n",logpath);
		return NULL;
	}

	avcodec_register_all();
	av_register_all();
	avfilter_register_all();

	av_log_set_level(AV_LOG_INFO);
	av_log_set_callback(log_cb);

	ctx = (struct GeneratorCtx *) malloc(sizeof(struct GeneratorCtx));
	if (!ctx)
	{
		av_log(ctx, AV_LOG_ERROR, "could not allocate context\n");
		fclose(flog);
		return NULL;
	}

	memset(ctx, 0, sizeof(struct GeneratorCtx));
	ctx->av_class = &av_generator_context_class;
	ctx->vg_install_path = vgm_i_path;

	ret = init_encoder(&ctx->oc, oname);
	if (ret < 0)
	{
		av_log(ctx, AV_LOG_ERROR, "could not initialize encoder with %s\n",oname);
		free(ctx);
		fclose(flog);
		return NULL;
	}
	ctx->InFrame = av_frame_alloc();
	ctx->OutFrame = av_frame_alloc();

	//Initialize filter to 0 pts
	ctx->OutFrame->pts = 0;

	ctx->audio.dec_frame = av_frame_alloc();

	return ctx;
}

extern "C" void dinit_generator(struct GeneratorCtx*ctx)
{
	unset_audio(&ctx->audio);
	dinit_encoder(&ctx->oc);
	//TODO remove double free from ffmpe library
	av_frame_free(&ctx->InFrame);
	av_frame_free(&ctx->OutFrame);
	av_frame_free(&ctx->audio.dec_frame);
#ifdef WINDOWS
	free(ctx->vg_install_path);
#endif
	free(ctx);
	fclose(flog);
}
